#!/usr/bin/perl

use Test;

BEGIN {
    $test_count            = 30;
    $test_hugepages        = 0;
    $test_exec_checking    = 0;
    $test_map_checking     = 0;
    $test_devzero_checking = 0;

    system("echo 1 > /proc/sys/vm/nr_hugepages 2> /dev/null");
    if ( system("grep -q 1 /proc/sys/vm/nr_hugepages 2> /dev/null") == 0 ) {
        $test_hugepages = 1;
        $test_count += 8;
    }

    if ( system("grep -q 0 /sys/fs/selinux/checkreqprot 2> /dev/null") == 0 ) {
        $test_exec_checking = 1;
        $test_count += 4;
    }

    if ( system("grep -q '/dev .*noexec' /proc/self/mounts 2> /dev/null") != 0 )
    {
        $test_devzero_checking = 1;
        $test_count += 4;
    }

    if ( -e '/sys/fs/selinux/class/file/perms/map' ) {
        $test_map_checking = 1;
        $test_count += 1;
    }

    plan tests => $test_count;
}

$basedir = $0;
$basedir =~ s|(.*)/[^/]*|$1|;

# Clean up from prior runs.
system "rm -f $basedir/temp_file";

# Create temporary file.
system "dd if=/dev/zero of=$basedir/temp_file count=8 2>&1 > /dev/null";
system "chcon -t test_mmap_file_t $basedir/temp_file";

# Test success and failure for mmap_zero.
$result = system "runcon -t test_mmap_zero_t $basedir/mmap_zero";
ok( $result, 0 );
$result = system "runcon -t test_no_mmap_zero_t $basedir/mmap_zero 2>&1";
ok($result);

# Test success and failure for execmem on mmap w/ anonymous private memory.
$result = system "runcon -t test_execmem_t $basedir/mmap_anon_private";
ok( $result, 0 );
$result = system "runcon -t test_no_execmem_t $basedir/mmap_anon_private 2>&1";
ok($result);

# Test success and failure for execmem on mprotect w/ anonymous private memory.
$result = system "runcon -t test_execmem_t $basedir/mprotect_anon_private";
ok( $result, 0 );
$result =
  system "runcon -t test_no_execmem_t $basedir/mprotect_anon_private 2>&1";
ok($result);

# Test success and failure for execmem on mmap w/ anonymous shared memory.
$result = system "runcon -t test_execmem_t $basedir/mmap_anon_shared";
ok( $result, 0 );
$result = system "runcon -t test_no_execmem_t $basedir/mmap_anon_shared 2>&1";
ok($result);

if ($test_devzero_checking) {

    # Test success and failure for mmap /dev/zero.
    $result =
      system
      "runcon -t test_mmap_dev_zero_t $basedir/mmap_file_shared /dev/zero";
    ok( $result, 0 );
    $result = system
"runcon -t test_no_mmap_dev_zero_t $basedir/mmap_file_shared /dev/zero 2>&1";
    ok($result);
}

# Test success and failure for mprotect w/ anonymous shared memory.
# In old kernels, this triggers a tmpfs file execute check.
# In new kernels, it triggers an execmem check.
$result =
  system "runcon -t test_mprotect_anon_shared_t $basedir/mprotect_anon_shared";
ok( $result, 0 );
$result = system
  "runcon -t test_no_mprotect_anon_shared_t $basedir/mprotect_anon_shared 2>&1";
ok($result);

if ($test_devzero_checking) {

    # Test success and failure for mprotect /dev/zero.
    $result = system
"runcon -t test_mprotect_dev_zero_t $basedir/mprotect_file_shared /dev/zero";
    ok( $result, 0 );
    $result = system
"runcon -t test_no_mprotect_dev_zero_t $basedir/mprotect_file_shared /dev/zero 2>&1";
    ok($result);
}

# Test success and failure for execheap, independent of execmem.
$result = system "runcon -t test_execheap_t $basedir/mprotect_heap";
ok( $result, 0 );
$result = system "runcon -t test_execmem_t $basedir/mprotect_heap 2>&1";
ok($result);

# Test success and failure for execstack, independent of execmem.
$result = system "runcon -t test_execstack_t $basedir/mprotect_stack";
ok( $result, 0 );
$result = system "runcon -t test_execmem_t $basedir/mprotect_stack 2>&1";
ok($result);

if ($test_exec_checking) {

# Repeat tests using personality(READ_IMPLIES_EXEC) instead of explicit PROT_EXEC.
    $result = system "runcon -t test_execstack_t -- $basedir/mprotect_stack -r";
    ok( $result, 0 );
    $result =
      system "runcon -t test_execmem_t -- $basedir/mprotect_stack -r 2>&1";
    ok($result);
}

# Test success and failure for thread execstack, independent of execmem.
$result =
  system "runcon -t test_execstack_t $basedir/mprotect_stack_thread pass";
ok( $result, 0 );
$result =
  system "runcon -t test_execmem_t $basedir/mprotect_stack_thread fail 2>&1";
ok($result);

# Test success and failure for file execute on mmap w/ file shared mapping.
$result = system
  "runcon -t test_file_rwx_t $basedir/mmap_file_shared $basedir/temp_file";
ok( $result, 0 );
$result = system
  "runcon -t test_file_rw_t $basedir/mmap_file_shared $basedir/temp_file 2>&1";
ok($result);

if ($test_exec_checking) {

# Repeat tests using personality(READ_IMPLIES_EXEC) instead of explicit PROT_EXEC.
    $result = system
"runcon -t test_file_rwx_t -- $basedir/mmap_file_shared -r $basedir/temp_file";
    ok( $result, 0 );
    $result = system
"runcon -t test_file_rw_t -- $basedir/mmap_file_shared -r $basedir/temp_file 2>&1";
    ok($result);
}

if ($test_map_checking) {
    $result = system
"runcon -t test_no_map_t -- $basedir/mmap_file_shared $basedir/temp_file 2>&1";
    ok($result);
}

# Test success and failure for file execute on mprotect w/ file shared mapping.
$result = system
  "runcon -t test_file_rwx_t $basedir/mprotect_file_shared $basedir/temp_file";
ok( $result, 0 );
$result = system
"runcon -t test_file_rw_t $basedir/mprotect_file_shared $basedir/temp_file 2>&1";
ok($result);

# Test success and failure for execmem on mmap w/ file private mapping.
$result = system
  "runcon -t test_execmem_t $basedir/mmap_file_private $basedir/temp_file";
ok( $result, 0 );
$result = system
"runcon -t test_no_execmem_t $basedir/mmap_file_private $basedir/temp_file 2>&1";
ok($result);

# Test success and failure for file execute on mprotect w/ file private mapping.
$result = system
"runcon -t test_file_rx_t $basedir/mprotect_file_private_rx $basedir/temp_file";
ok( $result, 0 );
$result = system
"runcon -t test_file_r_t $basedir/mprotect_file_private_rx $basedir/temp_file 2>&1";
ok($result);

# Test success and failure for execmem on mprotect w/ file private mapping.
$result = system
"runcon -t test_execmem_t $basedir/mprotect_file_private_rwx $basedir/temp_file";
ok( $result, 0 );
$result = system
"runcon -t test_no_execmem_t $basedir/mprotect_file_private_rwx $basedir/temp_file 2>&1";
ok($result);

# Test success and failure for file execmod on mprotect w/ file private mapping.
$result = system
"runcon -t test_execmod_t $basedir/mprotect_file_private_execmod $basedir/temp_file";
ok( $result, 0 );
$result = system
"runcon -t test_no_execmod_t $basedir/mprotect_file_private_execmod $basedir/temp_file 2>&1";
ok($result);

if ($test_hugepages) {

# Test success and failure for execmem on mmap w/ hugetlb anonymous shared memory.
    $result =
      system "runcon -t test_execmem_t $basedir/mmap_hugetlb_anon_shared";
    ok( $result, 0 );
    $result = system
      "runcon -t test_no_execmem_t $basedir/mmap_hugetlb_anon_shared 2>&1";
    ok($result);

# Test success and failure for execmem on mprotect w/ hugetlb anonymous shared memory.
    $result = system
"runcon -t test_mprotect_anon_shared_t $basedir/mprotect_hugetlb_anon_shared";
    ok( $result, 0 );
    $result = system
"runcon -t test_no_mprotect_anon_shared_t $basedir/mprotect_hugetlb_anon_shared 2>&1";
    ok($result);

# Test success and failure for execmem on mmap w/ hugetlb anonymous private memory.
    $result =
      system "runcon -t test_execmem_t $basedir/mmap_hugetlb_anon_private";
    ok( $result, 0 );
    $result = system
      "runcon -t test_no_execmem_t $basedir/mmap_hugetlb_anon_private 2>&1";
    ok($result);

# Test success and failure for execmem on mprotect w/ hugetlb anonymous private memory.
    $result = system
"runcon -t test_mprotect_anon_shared_t $basedir/mprotect_hugetlb_anon_private";
    ok( $result, 0 );
    $result = system
"runcon -t test_no_mprotect_anon_shared_t $basedir/mprotect_hugetlb_anon_private 2>&1";
    ok($result);

    system "echo 0 > /proc/sys/vm/nr_hugepages";
}

# Test success and failure for execmem on shmat SHM_EXEC.
$result = system "runcon -t test_execmem_t $basedir/shmat";
ok( $result, 0 );
$result = system "runcon -t test_no_execmem_t $basedir/shmat 2>&1";
ok($result);

# Clean up from prior runs.
system "rm -f $basedir/temp_file";

exit;
